/**
 * @fileOverview DOM Query Class
 * @author Xaephis <xaephis@gmail.com>
 * @version v 14.30 Mon Jun 13 2011 08:30:43 GMT+0800 (China Standard Time)
 */
Jx.declare('Jx.xml.SelectorQuery', {

	$$singleton: true,

	$$helpers: {

		'parser': 'Jx.xml.query.SelectorParser',

		'Dom': 'Jx.lib.Dom',

		'String': 'Jx.String',

		'Array': 'Jx.Array',

		'Browser': 'Jx.Browser'

	},

	$$callback: function(Constructor, Base, Instance){

		var Browser = Instance.Browser,

			overrides = {};


		//-- doesn't support dom collection to array
		if (Browser.supports('domCollectionToArray')){

			overrides.createCollection = function(context, fn){

				var types = fn.typeSelect,

					l = types.length,

					sliceFn = Array.prototype.slice,

					collection = [],

					cl = 0;

				if (types.indexOf('*') != -1){

					collection[cl++] = sliceFn.call(context.getElementsByTagName('*'), 0);

				} else {

					while(l--){

						collection.push.apply(collection,

							sliceFn.call(context.getElementsByTagName(types[l]), 0)

						);

					}

					collection = [collection];

				}

				return collection;

			};

		}

		return overrides;

	},

	genId: 0,

	typeGenId: 0,



	filterIndexes: undefined,

	validateIndexes: undefined,

	typeIndexes: undefined,

	debug: false,

	'compile-cache-gen-nodeId-property': 'node-cache-id',

	'compile-cache-elementIndex-property': 'element-index',

	nthChildRe: /^((([\-\+][1-9][0-9]*|[0-9]+|[\-\+])?n)(([\-\+])([0-9]+))?|([\+\-]?[1-9]|[1-9][0-9]*))$/,

	pseudoTypes: {

		':lang': 'type_attribute',

		':enabled': 'type_attribute',

		':disabled': 'type_attribute',

		':checked': 'type_attribute',

		':root': 'type_attribute'

	},

	constructor: function(){

		this.parser = Jx.instantiate(this.parser);

		this.filterIndexes = {};

		this.validateIndexes = {};

		this.typeIndexes = {};

		this.$baseCall(arguments);

	},

	getCompileIndexName: function(selector, type, create){

		var isFilter = type == 'filter',

			indexes = this[isFilter ? 'filterIndexes' : 'validateIndexes'],

			index = indexes.hasOwnProperty(selector) ?

				indexes[selector]

				:

				create ?

					(indexes[selector] = (isFilter ? 'compiledFilter' : 'compiledValidator')+(++this.genId))

					:

					null,

			ret;

		if (index){

			ret = index;

		}

		return ret;

	},

	getCompileTypeName: function(type){

		var indexes = this.typeIndexes;

		return (type in indexes) ?

			indexes[type]

			:

			indexes[type] = 'typeName'+(++this.typeGenId);

	},

/**
 *	compile codes
 */
	'compile-matchTagName': function(nodeVarName, name, ns){

		var Str = this.String,

			tag = ns != '*' && ns ? Str.escape(ns) : '';

		if (name){

			tag = (tag ? tag+':' : '') + Str.escape(name);

			return nodeVarName+'.nodeName == "'+tag+'"';

		} else if (tag){

			return nodeVarName+'.nodeName.substring(0, '+(tag.length+1)+') == "'+tag+':"';

		} else {

			return 'true';

		}

	},

	'compile-matchTagNameVar': function(nodeVarName, varName, ns){

		var Str = this.String,

			tag = ns != '*' && ns ? Str.escape(ns) : '';

		if (ns){

			return nodeVarName+'.nodeName.substring(0, '+varName+'.length + 1) == ('+varName+'.toUpperCase()+":")';

		} else {

			return nodeVarName+'.nodeName == '+varName+'.toUpperCase()';

		}

	},

	'compile-getAttribute': function(varName, name){

		return varName+'=node.getAttribute("'+this.String.escape(name)+'")';

	},

	'compile-attributeMatchers': function(varName, name, op, value){

		var escaped = this.String.escape(value),

			verify = this['compile-getAttribute'](varName, name),

			get = '(' + verify + ') && (';

		return [

			'(',

				op == '!=' ?

					'(!('+verify+') || ('+ varName+' != "'+escaped + '"))'

					:

					op == '=' ?

						get + varName+' == "'+escaped + '")'

						:

						op == '~=' ?

							get + '(" "+'+varName+'+" ").indexOf(" '+escaped+' ") != -1 )'

							:

							op == '|=' ?

								get + '("-"+'+varName+'+"-").indexOf("-'+escaped+'-") != -1)'

								:

								op == '^=' ?

									get + varName+'.substring(0, '+value.length+') == "'+escaped+'")'

									:

									op == '$=' ?

										get + varName+'.substring('+varName+'.length - '+value.length+', '+varName+'.length) == "'+escaped+'")'

										:

										'node.hasAttribute("'+this.String.escape(name)+'")',

			')'


		].join('');

	},

	'compile-getNodeProperty': function(nodeVarName, varName, propertyName){

		return varName+'='+nodeVarName+'.getAttribute('+propertyName+');';

	},
	'compile-setNodeProperty': function(nodeVarName, varName, propertyName, value){

		return varName ?

			nodeVarName+'.setAttribute("'+propertyName+'", '+varName+' = '+value+');'

			:

			nodeVarName+'.setAttribute("'+propertyName+'", '+value+');';

	},

	'compile-getNodePropertyIf': function(nodeVarName, varName, propertyName, value){

		var get = this['compile-getNodeProperty'](nodeVarName, varName, propertyName);

		return [ get,

			'if (!'+varName+'){',

				this['compile-setNodeProperty'](nodeVarName, varName, propertyName, value),

			'}'

		].join('\n');

	},

	'compile-getElementIndex': function(nodeVarName, assignVarName, vars){

		vars.push('child', 'nodeId', 'nodeCache', 'tag', 'pn',

			'cachedIndex=cache.indexed', 'cachedTypeIndex=cache.typeIndexed', 'typedLength',

			'typeNamePrefix', assignVarName, 'zero'+assignVarName

		);

		return [

			'pn='+nodeVarName+'.parentNode;',

			this['compile-getNodePropertyIf']('pn', 'nodeId', this['compile-cache-gen-nodeId-property'], '"jx-query-id"+(++this.genId)'),

			'typeNamePrefix = nodeId+"-item"',

			'if (!(nodeId in cachedIndex)){',

				assignVarName+'=0;',

				'typedLength = cachedTypeIndex.length;',

				'while( typedLength -- ){',

					'cachedIndex[typeNamePrefix+"-length-"+cachedTypeIndex[typedLength]] = 0;',

				'}',

				'for(child=pn.firstChild; child; child = child.nextSibling){',

					'if (child.nodeType == 1){',

						this['compile-setNodeProperty']('child', null, this['compile-cache-elementIndex-property'], '++'+assignVarName),

						'typedLength = cachedTypeIndex.length;',

						'while( typedLength -- ){',

							'tag = cachedTypeIndex[typedLength];',

							'cachedIndex[typeNamePrefix + tag +"-offset" + '+assignVarName+'] ='+this['compile-matchTagNameVar']('child', 'tag')+'?',

								'++cachedIndex[typeNamePrefix+"-length-"+ tag]',

								':',

								'cachedIndex[typeNamePrefix + tag +"-offset" + '+assignVarName+'] = 0;',

						'}',

					'}',

				'}',

				'cachedIndex[nodeId] = '+assignVarName+';',

			'}',

			this['compile-getNodeProperty'](nodeVarName, assignVarName, this['compile-cache-elementIndex-property']),

			'zero'+assignVarName+' = '+assignVarName+' - 1;'

			//'console.log("elementIndex: ", '+assignVarName+');'

		].join('\n');

	},

	'compile-pseudo-nth-parse': function(value){

		value = this.String.trim(value);

		if (value == 'odd'){

			value = '2n+1';

		} else if (value == 'even'){

			value = '2n';

		//-- remove spaces
		} else {

			value = value.replace(/[\r\n\t\s ]/g, '');

		}

		//-- validate value
		var m = value && value.match(this.nthChildRe),

			nthSn = '+',

			nth = '0', offset, ret;

		if (m){

			if (m[2]){

				nth = Jx.getProperty(m, 3);

				if (!nth || nth == '+' || nth == '0'){

					nth = '1';

				} else if (nth == '-'){

					nth = '-1';

				}

				if (m[4]){

					offset = m[6];

					nthSn = m[5];

				} else {

					offset = '0';

				}

			} else {

				offset = m[7];

			}

			//console.log('nth: ', nth, 'offset: "'+offset+'"', ' sn: ', nthSn);

			ret = [

				nth,

				offset,

				nthSn == '+' ?

					'-'

					:

					'+'

			];

		}

		return ret;

	},

/**
 *	Compile Pseudo Selector
 */

	'compile-pseudo[:first-child]': function(data, lexeme, marker, name, value){

		//-- create precode
		lexeme.preCode = this['compile-getElementIndex']('node', 'elementIndex', lexeme.vars);

		lexeme.code = '(elementIndex == 1)';

	},

	'compile-pseudo[:last-child]': function(data, lexeme, marker, name, value){

		//-- create precode
		lexeme.preCode = [

			this['compile-getElementIndex']('node', 'elementIndex', lexeme.vars),

			'lastElementIndex=cachedIndex[nodeId];'

		];

		lexeme.vars.push('lastElementIndex');

		lexeme.code = '(elementIndex == lastElementIndex)';

	},

	'compile-pseudo[:only-child]': function(data, lexeme, marker, name, value){

		//-- create precode
		lexeme.preCode = [

			this['compile-getElementIndex']('node', 'elementIndex', lexeme.vars),

			'lastElementIndex=cachedIndex[nodeId];'

		];

		lexeme.vars.push('lastElementIndex');

		lexeme.code = '(lastElementIndex == 1)';

	},

	'compile-pseudo[:nth-child]': function(data, lexeme, marker, name, value){

		var m = this['compile-pseudo-nth-parse'](value), offset, nth;

		if (m){

			nth = m[0];

			offset = m[1];

			if (nth == '0'){

				lexeme.code = '(elementIndex == '+offset+')';

			} else if (nth != '1'){

				lexeme.code = offset == '0' ?

					'!(elementIndex % '+nth+')'

					:

					lexeme.code = '!((elementIndex '+m[2]+' '+offset+') % '+nth+')';

			} else if (offset != '0'){

				lexeme.code = '(elementIndex >= '+offset+')';

			}

			//-- create precode
			if (lexeme.code){

				lexeme.preCode = this['compile-getElementIndex']('node', 'elementIndex', lexeme.vars);

			}

		}

		return !!m;

	},

	'compile-pseudo[:nth-last-child]': function(data, lexeme, marker, name, value){

		var m = this['compile-pseudo-nth-parse'](value),

			offset, nth, ret;

		if (m){

			nth = m[0];

			offset = m[1];

			if (nth == '0'){

				lexeme.code = '(lastElementOffset == '+offset+')';

			} else if (nth != '1'){

				lexeme.code = offset == '0' ?

					'!(lastElementOffset % '+nth+')'

					:

					lexeme.code = '!((lastElementOffset '+m[2]+' '+offset+') % '+nth+')';

			} else if (offset != '0'){

				lexeme.code = '(lastElementOffset >= '+offset+')';

			}

			//-- create precode
			if (lexeme.code){

				lexeme.vars.push('lastElementOffset');

				lexeme.preCode = [

					this['compile-getElementIndex']('node', 'elementIndex', lexeme.vars),

					'lastElementOffset = cachedIndex[nodeId] - (elementIndex  - 1);'

				];

			}

		}

		return !!m;

	},

	'compile-pseudo[:first-of-type]': function(data, lexeme, marker, name, value){

		var type = data.currentType,

			varName;


		//-- same as nth-child
		if (type == '*'){

			this['compile-pseudo[:first-child]'](data, lexeme, marker, name, value);

		} else {

			varName = this.getCompileTypeName(type);

			data.indexedPseudoTypes = this.Array.unique(data.indexedPseudoTypes.concat(type));

			lexeme.vars.push(varName, varName+'Length', 'typeIndexAccessName');

			//-- create precode
			lexeme.preCode = [

				this['compile-getElementIndex']('node', 'elementIndex', lexeme.vars),

				'typeIndexAccessName = typeNamePrefix + "'+type+'-offset" + elementIndex;',

				varName+'=typeIndexAccessName in cachedIndex ? cachedIndex[typeIndexAccessName] : 0;'

			];

			lexeme.code=varName+' == 1';

		}

	},

	'compile-pseudo[:last-of-type]': function(data, lexeme, marker, name, value){

		var type = data.currentType,

			varName;


		//-- same as nth-child
		if (type == '*'){

			this['compile-pseudo[:last-child]'](data, lexeme, marker, name, value);

		} else {

			varName = this.getCompileTypeName(type);

			data.indexedPseudoTypes = this.Array.unique(data.indexedPseudoTypes.concat(type));

			lexeme.vars.push(varName, varName+'Length', 'typeIndexAccessName');

			//-- create precode
			lexeme.preCode = [

				this['compile-getElementIndex']('node', 'elementIndex', lexeme.vars),

				'typeIndexAccessName = typeNamePrefix + "'+type+'-offset" + elementIndex;',

				varName+'=typeIndexAccessName in cachedIndex ? cachedIndex[typeIndexAccessName] : 0;',

				'typeIndexAccessName = typeNamePrefix + "-length-'+type+'";',

				varName+'Length=typeIndexAccessName in cachedIndex ? cachedIndex[typeIndexAccessName] 0 : 0;'

			];

			lexeme.code=+varName+'Length && '+varName+' == '+varName+'Length';

		}

	},

	'compile-pseudo[:nth-of-type]': function(data, lexeme, marker, name, value){

		var type = data.currentType,

			varName, m, offset, nth, ret;


		//-- same as nth-child
		if (type == '*'){

			ret = this['compile-pseudo[:nth-child]'](data, lexeme, marker, name, value);

		} else {

			ret = !!(m = this['compile-pseudo-nth-parse'](value));

			if (m){

				nth = m[0];

				offset = m[1];

				varName = this.getCompileTypeName(type);

				if (nth == '0'){

					lexeme.code = '('+varName+' == '+offset+')';

				} else if (nth != '1'){

					lexeme.code = offset == '0' ?

						'!('+varName+' % '+nth+')'

						:

						lexeme.code = '!(('+varName+' '+m[2]+' '+offset+') % '+nth+')';

				} else if (offset != '0'){

					lexeme.code = '('+varName+' >= '+offset+')';

				}

				if (lexeme.code){

					data.indexedPseudoTypes = this.Array.unique(data.indexedPseudoTypes.concat(type));

					lexeme.vars.push(varName, 'typeIndexAccessName');

					lexeme.preCode = [

						this['compile-getElementIndex']('node', 'elementIndex', lexeme.vars),

						'typeIndexAccessName = typeNamePrefix + "'+type+'-offset" + elementIndex;',

						varName+'=typeIndexAccessName in cachedIndex ? cachedIndex[typeIndexAccessName] : 0;'

					];

				}

			}

		}

		return ret;

	},

	'compile-pseudo[:nth-last-of-type]': function(data, lexeme, marker, name, value){

		var type = data.currentType,

			varName, m, offset, nth, ret;


		//-- same as nth-child
		if (type == '*'){

			ret = this['compile-pseudo[:nth-last-child]'](data, lexeme, marker, name, value);

		} else {

			ret = !!(m = this['compile-pseudo-nth-parse'](value));

			if (m){

				nth = m[0];

				offset = m[1];

				varName = this.getCompileTypeName(type);

				if (nth == '0'){

					lexeme.code = varName+'Length && ('+varName+'lastElementOffset == '+offset+')';

				} else if (nth != '1'){

					lexeme.code = offset == '0' ?

						varName+'Length && !('+varName+'lastElementOffset % '+nth+')'

						:

						lexeme.code = varName+'Length && !(('+varName+'lastElementOffset '+m[2]+' '+offset+') % '+nth+')';

				} else if (offset != '0'){

					lexeme.code = varName+'Length && ('+varName+'lastElementOffset >= '+offset+')';

				}

				if (lexeme.code){

					data.indexedPseudoTypes = this.Array.unique(data.indexedPseudoTypes.concat(type));

					lexeme.vars.push(varName, varName+'Length', varName+'lastElementOffset', 'typeIndexAccessName');

					//-- create precode
					lexeme.preCode = [

						this['compile-getElementIndex']('node', 'elementIndex', lexeme.vars),

						'typeIndexAccessName = typeNamePrefix + "'+type+'-offset" + elementIndex;',

						varName+'=typeIndexAccessName in cachedIndex ? cachedIndex[typeIndexAccessName] : 0;',

						'typeIndexAccessName = typeNamePrefix + "-length-'+type+'";',

						varName+'Length=typeIndexAccessName in cachedIndex ? cachedIndex[typeIndexAccessName] : 0;',

						varName+'lastElementOffset = '+varName+'Length - ('+varName+' - 1);'

					];

				}

			}

		}

		return ret;

	},

	'compile-pseudo[:only-of-type]': function(data, lexeme, marker, name, value){

		var type = data.currentType,

			varName;


		//-- same as nth-child
		if (type == '*'){

			this['compile-pseudo[:only-child]'](data, lexeme, marker, name, value);

		} else {

			varName = this.getCompileTypeName(type);

			data.indexedPseudoTypes = this.Array.unique(data.indexedPseudoTypes.concat(type));

			lexeme.vars.push(varName, varName+'Length', 'typeIndexAccessName');

			//-- create precode
			lexeme.preCode = [

				this['compile-getElementIndex']('node', 'elementIndex', lexeme.vars),

				'typeIndexAccessName = typeNamePrefix + "-length-'+type+'";',

				varName+'Length=typeIndexAccessName in cachedIndex ? cachedIndex[typeIndexAccessName] : 0;'

			];

			lexeme.code=varName+'Length == 1';

		}

	},

	'compile-pseudo[:empty]': function(data, lexeme, marker, name, value){

		lexeme.vars.push('child', 'nodeId',

			'cachedIndex=cache.indexed', 'emptyNodeAccess', 'nodeIsEmpty', 'nodeType'

		);


		lexeme.preCode = [

			this['compile-getNodePropertyIf']('node', 'nodeId', this['compile-cache-gen-nodeId-property'], '"jx-query-id"+(++this.genId)'),

			'emptyNodeAccess = nodeId+"-empty";',

			'if (!(emptyNodeAccess in cachedIndex)){',

				'cachedIndex[emptyNodeAccess]=true;',

				//-- find none
				'for(var n=node.firstChild; n ;n = n.nextSibling){',

					'nodeType = n.nodeType',

					'if (nodeType == 1 || nodeType == 3 || nodeType == 4 || nodeType == 5){',

						'cachedIndex[emptyNodeAccess]=false;',

						'break;',

					'}',

				'}',

				//-- apply to parent nodes
				'for(var p=node.parentNode, pid, nid; p && p !== context; p = p.parentNode){',

					this['compile-getNodePropertyIf']('p', 'nid', this['compile-cache-gen-nodeId-property'], '"jx-query-id"+(++this.genId)'),

					'pid = nid+"-empty";',

					'if (pid in cachedIndex){',

						'break;',

					'}',

					'cachedIndex[pid] = false;',

				'}',

			'}',

			'nodeIsEmpty = cachedIndex[emptyNodeAccess];'


		].join('\n');

		lexeme.code='nodeIsEmpty';

	},

	'compile-pseudo[:not]': function(data, lexeme, marker, name, value){

		var compiledName, indexName, ret;

		if (value){

			data.toCompile.push(value);

			compiledName = this.getCompileIndexName(value, 'validator', true);

			lexeme.vars.push(indexName = compiledName+'TypeCache');

			lexeme.preCode = indexName+' = this.Array.unique(this.'+compiledName+'.pseudoTypeIndexes.concat(cache.typeIndexed));';

			lexeme.code = '!this.' + compiledName + '(node,context,'+indexName+')';

		}

		return !!value;

	},

	// TODO: find [lang!="value"] to parent nodes
	'compile-pseudo[:lang]': function(data, lexeme, marker, name, value){

		var selector = '[lang|="'+this.String.escape(value)+'"]',

			compiledName = this.getCompileIndexName(selector, 'validator', true),

			indexName;

		data.toCompile.push(selector);

		indexName = compiledName+'Language';

		lexeme.vars.push(indexName, 'nodeLangAccess', 'cachedIndex=cache.indexed', 'subcollection', 'p', indexName+'foundLang');

		//-- TODO: optimize!!!!

		lexeme.preCode = [

			indexName+' = this.Array.unique(this.'+compiledName+'.pseudoTypeIndexes.concat(cache.typeIndexed));',

			[

				this['compile-getNodePropertyIf']('node', 'nodeId', this['compile-cache-gen-nodeId-property'], '"jx-query-id"+(++this.genId)'),

				'nodeLangAccess = nodeId+"-lang-'+value+'";',

				'if (!(nodeLangAccess in cachedIndex)){',

					indexName+'foundLang = false;',

					'p = node;',

					//-- find language from current node up to parent nodes
					'for(var pid, nid; p && p.nodeType == 1; p = p.parentNode){',

						this['compile-getNodeProperty']('p', 'nid', this['compile-cache-gen-nodeId-property'])+';',

						'if ((pid = nid+"-lang-'+value+'") in cachedIndex){',

							indexName+'foundLang = cachedIndex[nodeLangAccess] = cachedIndex[pid];',

							'break;',

						'} else if (this.'+compiledName + '(p,context,'+indexName+')){',

							indexName+'foundLang = true;',

							//-- apply to all nodes
							'subcollection = p.getElementsByTagName("'+this.String.escape(this.compileData.currentType)+'");',

							'for(var sl=subcollection.length; sl--;){',

								'p=subcollection[sl];',

								this['compile-getNodePropertyIf']('p', 'nid', this['compile-cache-gen-nodeId-property'], '"jx-query-id"+(++this.genId)'),

								'cachedIndex[nid+"-lang-'+value+'"] = true;',

							'}',

							'break;',

						'}',

					'}',

				'} else {',

					indexName+'foundLang = cachedIndex[nodeLangAccess];',

				'}',

			].join('\n')

		];

		lexeme.code = indexName+'foundLang';

		return !!value;

	},

	'compile-pseudo[:root]': function(data, lexeme, marker, name, value){

		lexeme.ltype = 'type_attribute';

		lexeme.code = '(node === cache.docRoot)';

	},

	'compile-pseudo[:enabled]': function(data, lexeme, marker, name, value){

		var varName = 'attrValue';

		lexeme.vars = [varName];

		lexeme.ltype = 'type_attribute';

		lexeme.code = this['compile-attributeMatchers'](varName, 'disabled', '!=', 'disabled');

	},

	'compile-pseudo[:disabled]': function(data, lexeme, marker, name, value){

		var varName = 'attrValue';

		lexeme.vars = [varName];

		lexeme.ltype = 'type_attribute';

		lexeme.code = this['compile-attributeMatchers'](varName, 'disabled', '=', 'disabled');

	},

	'compile-pseudo[:checked]': function(data, lexeme, marker, name, value){

		var varName = 'attrValue';

		lexeme.vars = [varName];

		lexeme.ltype = 'type_attribute';

		lexeme.code = this['compile-attributeMatchers'](varName, 'checked', '=', 'checked');

	},







	//getCompileIndexName: function(selector, type, create)


/**
 *	compile lexemes
 */
	'compile-rule-Type_Selector': function(lexeme, values){

		var varName = 'tag',

			l = values.length,

			name = '*', ns = '*';

		//-- "tagname"
		if (l == 1){

			name = values[0].value;

		//-- "|" "tagname"
		} else if (l == 2){

			name = value[1].value;


		//-- "namespace" "|" "tagname"
		} else if (l == 3){

			ns = value[0].value;

			name = value[2].value;

		}

		lexeme.vars = [varName];

		lexeme.ltype = 'type_selector';

		lexeme.tag = name;

		lexeme.ns = ns;

		this.compileData.currentType = name;

	},

	'compile-rule-Hash': function(lexeme, values){

		var varName = 'attrValue';

		lexeme.vars = [varName];

		lexeme.ltype = 'type_attribute';

		lexeme.code = this['compile-attributeMatchers'](varName, 'id', '=', values[1].value);

	},

	'compile-rule-Class': function(lexeme, values){

		var varName = 'attrValue';

		lexeme.vars = [varName];

		lexeme.ltype = 'type_attribute';

		lexeme.code = this['compile-attributeMatchers'](varName, 'class', '~=', values[1].value);

	},

	'compile-rule-Attribute': function(lexeme, values){

		var varName = 'attrValue', code,

			name = values[1].value,

			parts = this.compileData.parts,

			attribs = parts.attributes,

			ops = parts.attributeOperators,

			op = '',

			value = '';

		//-- attribute match
		if (values.length > 3){

			op = values[2].value;

			value = values[3].value;

			if (ops.indexOf(name) == -1){

				ops[ops.length] = name;

			}

		}

		lexeme.vars = [varName];

		lexeme.ltype = 'type_attribute';

		lexeme.code = this['compile-attributeMatchers'](varName, name, op, value);

		if (attribs.indexOf(name) == -1){

			attribs[attribs.length] = name;

		}


	},

	'compile-rule-Pseudo': function(lexeme, values){

		var marker = values[0].value,

			name = values[1].value,

			access = marker+name,

			hasParameter = values.length > 2,

			value = '',

			parts = this.compileData.parts.pseudos,

			compilerName = 'compile-pseudo['+access+']', vs, ret;

		//-- create value
		if (hasParameter){

			vs = values[3].value;

			for(var c=0, l=vs.length;c<l;c++){

				value+=vs[c].value;

			}
		}

		lexeme.vars = [];

		if (compilerName in this){

			ret = this[compilerName](this.compileData, lexeme, marker, name, value);

		}

		if (!lexeme.ltype){

			lexeme.ltype = 'type_pseudo';

		}

		if (parts.indexOf(access) == -1){

			parts[parts.length] = access;

		}

		return ret;

	},

	'compile-rule-Type_Attribute': function(lexeme, values){

		//-- create codes
		var code = [],

			l = values.length,

			type_attribute = values[0];

		if (values.length == 2){

			lexeme.attrs = values[0].attrs.slice(0);

			type_attribute = values[1];

		} else {

			lexeme.attrs = [];

		}

		lexeme.ltype = 'type_attributes';

		lexeme.attrs.push({

			vars: type_attribute.vars,
			ltype: type_attribute.ltype,
			code: type_attribute.code,
			preCode: type_attribute.preCode

		});

	},

	'compile-rule-Simple_Selector': function(lexeme, values){

		var Arr = this.Array,

			l = values.length,

			set = {
				vars: [],
				combinator: ' ',
				tag: '',
				ns: '',
				typeCode: '',

				preAttrMatch: [],
				attrMatch: [],

				prePseudoMatch: [],
				pseudoMatch: [],
				code: ''
			},

			vars = set.vars,

			data = this.compileData,

			sets = data.sets,

			attrMatch = set.attrMatch,

			pseudoMatch = set.pseudoMatch,

			preAttrMatch = set.preAttrMatch,

			prePseudoMatch = set.prePseudoMatch,

			selector = lexeme.getCode(),

			fnName = this.getCompileIndexName(selector, 'validator', true),

			matchCode = [],

			matchCodeIndex = 0,

			simpleMatch = '',

			tag, ns, typeMatch, v, code, matchVars, preMatch, attrs, attr;

		while(l--){

			v = values[l];


			if (v.ltype == 'type_selector'){

				matchVars = v.vars;

				if (matchVars){

					vars.push.apply(vars, matchVars);

				}

				tag = v.tag;

				ns = v.ns;

			} else if (v.ltype == 'type_attributes'){

				attrs = v.attrs;

				for(var c=0, attrl = attrs.length; c<attrl; c++){

					attr = attrs[c];

					code = attr.code;

					preMatch = attr.preCode;

					matchVars = attr.vars;

					if (matchVars){

						vars.push.apply(vars, matchVars);

					}

					if (attr.ltype == 'type_pseudo'){

						if (code){

							if (preMatch){

								if (Jx.isArray(preMatch)){

									prePseudoMatch.push.apply(prePseudoMatch, preMatch);

								} else {

									prePseudoMatch.push(preMatch);

								}

							}

							if (Jx.isArray(code)){

								pseudoMatch.push.apply(pseudoMatch, code);

							} else {

								pseudoMatch.push(code);

							}

						}


					//-- type_attribute
					} else if (attr.ltype == 'type_attribute'){

						if (code){

							if (preMatch){

								if (Jx.isArray(preMatch)){

									preAttrMatch.push.apply(preAttrMatch, preMatch);

								} else {

									preAttrMatch.push(preMatch);

								}

							}

							if (Jx.isArray(code)){

								attrMatch.push.apply(attrMatch, code);


							} else {

								attrMatch.push(code);

							}
							//console.log('attr match! ', {attrMatch: attrMatch});

						}

					}



				}


			}

		}

		set.tag = tag || '*';

		set.ns = ns || '*';

		set.simple = selector;

		sets[l = sets.length] = set;

		set.name = 'setId'+l;

		//-- compile simple selector
		if (!this.hasOwnProperty(fnName)){

			//console.log('simple selector: ', {match: attrMatch.slice(0)});

			attrMatch = attrMatch.slice(0);

			if (set.tag != '*' || set.ns != '*') {

				attrMatch.unshift('('+this['compile-matchTagName']('node', set.tag, set.ns)+')');

			}

			//-- compile type and attribute match
			if (attrMatch.length){

				simpleMatch = '';

				if (preAttrMatch.length){

					simpleMatch = Arr.unique(preAttrMatch).join('\n');

				}

				matchCode[matchCodeIndex++] = simpleMatch+' if ('+Arr.unique(attrMatch).join(' && ')+'){';

			}

			//-- compile pseudo match
			if (pseudoMatch.length){

				simpleMatch = '';

				if (prePseudoMatch.length){

					simpleMatch = Arr.unique(prePseudoMatch).join('\n');

				}

				matchCode[matchCodeIndex++] = simpleMatch+' if ('+Arr.unique(pseudoMatch).join(' && ')+'){';

			}


			if (matchCodeIndex){

				matchCode[matchCodeIndex-1]+=' return true; ';

			}



			this[fnName] = new Function('node,context,cache', [

				vars.length ?

					'var '+Jx.Array.unique(vars).join(',')+';'

					:

					'',

				matchCodeIndex ?

					matchCode.join('\n')+this.String.repeat('}', matchCodeIndex)+' return false; '

					:

					'return true;'

			].join('\n'));

			this.onCompile(true, fnName, selector, lexeme);

			this[fnName].pseudoTypeIndexes = data.indexedPseudoTypes;


		}

	},

	'compile-rule-Selector': function(lexeme, values){

		var code,

			data = this.compileData,

			sets = data.sets,

			setLength = sets.length,

			combinatorParts = data.parts.combinators,

			set = sets[setLength-1],

			completeCode = false,

			leftSelector,

			selectorIndexName,

			selector, simple, combinator,

			selectorSet, simpleSet,

			selectorFnName,

			simpleSelectorFnName,

			withSimpleSelectorFnName,

			withoutSimpleSelectorFnName;


		//-- selector combinator simple_selector
		if (values.length == 3){

			simple = values[2];

			combinator = values[1].production;

			if (combinator == 'whitespace'){

				combinator = ' ';

			}

			selectorSet = sets[setLength - 2];

			selectorSet.combinator = combinator;

			simpleSet = sets[setLength - 1];

			simpleSet.before = values[0].getCode()+combinator;


			if (combinatorParts.indexOf(combinator) == -1){

				combinatorParts[combinatorParts.length] = combinator;

			}




		//-- selector
		}

		selector = set.selector = lexeme.getCode();

		leftSelector = simpleSet ? simpleSet.before : selector;


		if (combinator){

			withSimpleSelectorFnName = this.getCompileIndexName(selector, 'validator', true);

			withoutSimpleSelectorFnName = this.getCompileIndexName(simpleSet.before, 'validator', true);

			selectorFnName = this.getCompileIndexName(selectorSet.selector, 'validator', true);

			simpleSelectorFnName = this.getCompileIndexName(simpleSet.simple, 'validator', true);


			//-- child of
			if (combinator == '>'){

				code = [

					'var root = cache.docRoot;',

					'if (node !== root){',

						'return this.'+selectorFnName+'(node.parentNode, context, cache);',

					'}'

				].join('\n');


			//-- anchor
			} else if (combinator == '~'){

				code = [

					'for(node = node.previousSibling; node; node = node.previousSibling){',

						'if (node.nodeType == 1 && this.'+selectorFnName+'(node, context, cache)){',

							'return true;',

						'}',

					'}'

				].join('\n');

			//-- plus
			} else if (combinator == '+'){

				code = [

					'while((node = node.previousSibling) && node.nodeType != 1);',

					'if (node){',

						'return this.'+selectorFnName+'(node, context, cache);',

					'}'

				].join('\n');

			//-- descendant
			} else if (combinator == ' '){

				selectorIndexName = this.getCompileIndexName(leftSelector, 'validator', true);

				completeCode = true;

				code = [

					'var access = "up-'+selectorIndexName+'",',

						'sets = cache.sets,',

						'root = cache.docRoot,',

						'cachedIndex = access in sets ? sets[access] : sets[access] = {},',

						'nodes = [], ni = 0, nodeId, access, l, found = false;',

					'if (node !== root){',

						'for(node = node.parentNode; node; node = node.parentNode){',

							this['compile-getNodePropertyIf']('node', 'nodeId', this['compile-cache-gen-nodeId-property'], '"jx-query-id"+(++this.genId)'),

							'if (nodeId in cachedIndex){',

								'found = cachedIndex[nodeId]; break;',

							'} else {',

								'nodes[ni++] = nodeId;',

								'if (this.'+selectorFnName+'(node, context, cache)){',

									'found = true; break;',

								'}',

							'}',

							'if (node === root){ break; }',

						'}',

						'l = nodes.length;',

						'while(l--){',

							'cachedIndex[nodes[l]] = found;',

						'}',

					'}',

					'return found;'

				].join('\n');

			}

			//-- compile without simple selector
			if (!(withoutSimpleSelectorFnName in this)){

				this[withoutSimpleSelectorFnName] = new Function('node,context,cache', [

					code,

					completeCode ? '' : 'return false;'


				].join('\n'));

				this.onCompile(true, withoutSimpleSelectorFnName, simpleSet.before, lexeme);

				this[withoutSimpleSelectorFnName].pseudoTypeIndexes = data.indexedPseudoTypes;

			}

			//-- compile with simple selector
			if (!(withSimpleSelectorFnName in this)){

				this[withSimpleSelectorFnName] = new Function('node,context,cache' , [

					'if (this.'+simpleSelectorFnName+'(node, context, cache)){',

						code,

					'}',

					'return false;'

				].join('\n'));

				this.onCompile(true, withSimpleSelectorFnName, selector, lexeme);

				this[withSimpleSelectorFnName].pseudoTypeIndexes = data.indexedPseudoTypes;


			}

		}

	},

	'compile-rule-Group': function(lexeme, values){

		var Arr = this.Array,

			data = this.compileData,

			sets = data.sets,

			set = sets.pop(),

			preAttrs = set.preAttrMatch,

			attrs = set.attrMatch,

			prePseudos = set.prePseudoMatch,

			pseudos = set.pseudoMatch,

			code = [],

			group = {

				tag: set.tag,

				ns: set.ns,

				simpleMatchCode: '',

				sets: [],

				groupSelectorFnName: set.before ?

					this.getCompileIndexName(set.before, 'validator', true)

					:

					''

			},

			simpleMatch = '',

			simpleMatchCode = [],

			simpleMatchCodeIndex = 0;

		data.groupVars.push.apply(data.groupVars, set.vars);

		//-- include type match if ns is available
		if (set.ns != '*'){

			typeMatch = '('+this['compile-matchTagName']('node', set.tag, set.ns)+')';

			if (typeMatch){

				attrs.unshift(typeMatch);

			}

		}

		//-- type and attribute match
		if (attrs.length){

			simpleMatch = '';

			if (preAttrs.length){

				simpleMatch+=Arr.unique(preAttrs).join('\n')+'\n';

			}

			simpleMatchCode[ simpleMatchCodeIndex++ ] = simpleMatch+' if ('+Arr.unique(attrs).join(' && ')+'){';

		}

		//-- pseudo match
		if (pseudos.length){

			simpleMatch = '';

			if (prePseudos.length){

				simpleMatch+=Arr.unique(prePseudos).join('\n')+'\n';

			}

			simpleMatchCode[ simpleMatchCodeIndex++ ] = simpleMatch+' if ('+Arr.unique(pseudos).join(' && ')+'){'

		}


		if (simpleMatchCodeIndex){

			simpleMatchCode[simpleMatchCodeIndex-1] +=

				group.groupSelectorFnName ?

					['if (this.'+group.groupSelectorFnName+'(node, context, cache)){',

						'result[ri++] = node;',

						//'if (ri > limit){',
						//
						//	'break;',
						//
						//'}',

						'continue;',

					'}'].join('\n')

					:

					[

						'result[ri++] = node;',

						//'if (ri > limit){',
						//
						//	'break;',
						//
						//'}',

						'continue;'

					].join('\n');

		} else if (group.groupSelectorFnName){

			simpleMatchCode[simpleMatchCodeIndex++] = [

				'if (this.'+group.groupSelectorFnName+'(node, context, cache)){',

					'result[ri++] = node;',

					'if (ri > limit){',

						//'console.log("break limit! ", ri, " > ", limit);',

						'break;',

					'}',

					'continue;'

			].join('\n');

		}

		group.simpleMatchCode = simpleMatchCodeIndex ?

			simpleMatchCode.join('\n')+ this.String.repeat('}', simpleMatchCodeIndex)

			:

			'';

		if (!data.groupsByTagName.hasOwnProperty(set.tag)){

			data.groupsByTagName[set.tag] = [];

			data.types.push(set.tag);

		}

		data.groupsByTagName[set.tag].push(data.groups.length);

		data.groups.push(group);

		//-- delete all sets
		group.sets.push.apply(group.sets,

			sets.splice(0, sets.length)

		);

		lexeme.group = group;

		lexeme.currentType = data.currentType;

		//-- reset current type
		data.currentType = '*';

	},

	'compile-rule-Query': function(lexeme, values){

		var data = this.compileData,

			groups = data.groups,

			vars = data.groupVars,

			groupSelector = lexeme.getCode(),

			groupSelectorFilterFnName = this.getCompileIndexName(groupSelector, 'filter', true),

			code = [],

			codeIndex = 0,

			types = data.types,

			oneCollection = types.indexOf('*') != -1 || types.length == 1,

			//isMultiTypes = types.indexOf('*') != -1 || types.length > 1,

			tag, ns, groupsByTagName, groupsByTagNameCode,

			matchCode, group, groupSelectorFnName, fn;


		data.filterFnName = groupSelectorFilterFnName;

		//console.log('compile filter has filter? ', groupSelectorFilterFnName, ' = ', (groupSelectorFilterFnName in this), ' to string: ', this[groupSelectorFilterFnName].toString());

		if (!(groupSelectorFilterFnName in this)){

			types.sort();

			//-- create group code
			for(var c=0, l=types.length;c<l;c++){

				tag = types[c];

				groupsByTagName = data.groupsByTagName[tag];

				groupsByTagNameCode = [];

				for(var cg=0, lg=groupsByTagName.length;cg<lg;cg++){

					group = groups[groupsByTagName[cg]];

					matchCode = group.simpleMatchCode;

					if (!matchCode){

						groupsByTagNameCode = [[

							'result[ri++] = node;',

							//'if (ri > limit){',
							//
							//	'break;',
							//
							//'}',

							'continue;'

						].join('\n')];

						break;

					} else {

						groupsByTagNameCode.push(matchCode);

					}

				}

				if (l > 1){

					//-- create type match
					if (tag == '*'){

						code[codeIndex++] = groupsByTagNameCode.join('\n');

					} else {

						code[codeIndex++] = [

								'if ('+this['compile-matchTagName']('node', tag, '*')+'){',

									groupsByTagNameCode.join('\n'),

								'}'

						].join('\n');

						codeIndex = code.length;

					}

				} else {

					code[codeIndex++] = groupsByTagNameCode.join('\n');

				}

			}

			code.unshift(

				'for(var c=0, l=collection.length; c<l && ri<=limit; c++){',

					'node = collection[c];'

			);

			code.push( '}' );

			if (oneCollection){

				code.unshift('collection=collections[0];');

			} else {



				code.unshift(

					'for(var cl=0,collectionLength = collections.length; cl<collectionLength;cl++){',

						'collection = collections[cl];'

				);

				code.push('}');

			}

			vars.push('ui', 'node', 'nodeId');

			this[groupSelectorFilterFnName] = new Function('collections,context,limit,cache', [

					'var result = [],',

						'ri = 0,',

						'collection;',

					vars.length ?

						'var '+this.Array.unique(vars).join(', ')+';'

						:

						'',

					code.join('\n'),

					'return result;',

			].join('\n'));


			this.onCompile(false, groupSelectorFilterFnName, groupSelector, lexeme);


			(fn = this[groupSelectorFilterFnName]).typeSelect = data.types;

			fn.pseudoTypeIndexes = data.indexedPseudoTypes;


		}

	},

	onStartCompile: function(parser, query, data){

		parser.parse(query, this.onParse, this);

		//-- report query error
		if (parser.failed){

			console.warn('parser error invalid selector "'+query+'"');

		}

	},

	onCompile: function(isValidator, name, selector, lexeme){

		if (this.debug){

			console.log('"'+selector+'" ',name, ':', this[name].toString());

		}

	},

	onParse: function(production, lexeme){

		var name = 'compile-rule-'+production;

		if (Jx.inObject(this, name)){

			//console.log('reduced ', production, ' "',lexeme.getCode(),'"', lexeme);

			if (this[name](lexeme, lexeme.value) === false){

				this.parser.failed = true;

			}

		}

	},

	onBeforeSelect: function(fnName, fn){

	},

	onInitCache: function(cache, fn, query, context){

		Jx.assign(cache, {

			indexed: {},

			typeIndexed: fn.pseudoTypeIndexes,

			sets: {},

			docRoot: (context.nodeType == 9 ? context : context.ownerDocument).documentElement

		});

	},

	onClearCache: function(cache, fn, query, context){

		delete cache.docRoot;

	},

	compile: function(query){

		var data = this.compileData = {

				vars: [],

				sets: [],

				types: [],

				toCompile: [],

				currentType: '*',

				indexedPseudoTypes: [],

				groupsByTagName: {},

				groups: [],

				groupVars: [],

				parts: {

					combinators: [],

					tags: [],

					attributeOperators: [],

					attributes: [],

					pseudos: []

				},

				filterFn: null,

				filterFnName: null

			}, compileNext, l, varName, ret;

		this.onStartCompile(this.parser, query, data);

		compileNext = this.Array.unique(data.toCompile);

		ret = !this.parser.failed && data.filterFnName;

		Jx.each(data, function(n, v, o){

			if (o.hasOwnProperty(n)){

				delete o[n];

			}

		});

		delete this.compileData;

		if (ret){

			//-- compile others
			l = compileNext.length;

			while(l--){

				varName = this.getCompileIndexName(query = compileNext[l], 'filter', true);

				if (!(varName in this)){

					this.compile(query);

				}

			}

		}

		return ret;

	},

	getLimitOffset: function(offset, limit, all){

		//-- create offset/limit
		offset = Jx.isNumber(offset) ? Math.max(Math.min(offset, all), 0) : 0;

		limit = Jx.isNumber(limit) ? Math.max(Math.min(limit, all), 0) : 0;


		if (offset){

			if (!limit){

				limit = offset;

				offset = 0;

			}

			limit = offset + limit - 1;

		}

		return [offset, limit || all];

	},

	getNodeName: function(dom){

		return dom.nodeName;

	},

	getNodeNameMatch: function(name){

		return name;

	},

	createCollection: function(context, fn){

		var types = fn.typeSelect,

			l = types.length,

			collection = [],

			cl = 0;

		if (types.indexOf('*') != -1){

			collection[cl++] = context.getElementsByTagName('*');

		} else {

			while(l--){

				collection[cl++] = context.getElementsByTagName(types[l]);

			}
		}

		return collection;

	},

	is: function(query, dom){

		var ret = false, fn, fnName, cache, doc, docRoot;

		if (Jx.isString(query) &&

			(query = this.String.trim(query)) &&

			this.Dom.isElement(dom, 1)

		){

			fnName = this.getCompileIndexName(query, 'validator');

			if (!fnName){

				this.compile(query);

				fnName = this.getCompileIndexName(query, 'validator');

			}

			if (fnName){

				this.onInitCache(cache = {}, fn = this[fnName], query, doc = this.Dom.getDocument(dom));

				ret = this[fnName](dom, doc, cache);



				this.onClearCache(cache, fn, query, doc);

				delete cache.docRoot;

			}

		}

		return ret;

	},

	select: function(query, context, offset, limit){

		context = context || document;

		var result = [],

			collectionLength = 0,

			fnName, fn, collection, l, cache, root, bounds;

		if (Jx.isString(query) &&

			(query = this.String.trim(query)) &&

			this.Dom.isDom(context, 1, 9)

		){

			fnName = this.getCompileIndexName(query, 'filter');

			if (!fnName){

				fnName = this.compile(query);

			}

			//console.log('found fn name: ', fnName);

			if (fnName){

				this.onBeforeSelect(fnName, this[fnName]);

				fn = this[fnName];

				collection = this.createCollection(context, fn);

				l = collection.length;

				while(l--){

					collectionLength+=collection[l].length;

				}

				bounds = this.getLimitOffset(offset, limit, collectionLength);

				offset = bounds[0];

				limit = bounds[1];

				this.onInitCache(cache = {}, fn, query, context);

				result = this[fnName](collection, context, limit, cache);

				this.onClearCache(cache, fn, query, context);

				if (offset){

					return result.slice(offset);

				}

			}

		}

		return result;

	},

	filter: function(collection, query, offset, limit){

		var toFilter = [],

			fnName = this.getCompileIndexName(query, 'filter') || this.compile(query),

			Dom = this.Dom,

			result = [],

			typeIndexes = {},

			noTypeFilters = false,

			typeFilters,

			tfi = 0,

			context, dom, bounds, cache, fn;


		if (fnName){

			fn = this[fnName];

			if (Dom.isElement(collection, 1)){

				collection = [collection];

			}

			if (Jx.isArray(collection) || collection.length){

				typeFilters = this[fnName].typeSelect;

				if (typeFilters.indexOf('*') != -1 || !typeFilters.length){

					noTypeFilters = true;

				} else {

					for(var l=typeFilters.length; l--;){

						typeIndexes[

							this.getNodeNameMatch(typeFilters[l])

						] = true;

					}

				}

				for(var c = 0, l=collection.length;c<l;c++){

					dom = collection[c];

					if (Dom.isElement(dom) &&

						noTypeFilters || typeIndexes.hasOwnProperty(this.getNodeName(dom))

					){

						if (!context){

							context = dom.ownerDocument;

						}

						toFilter[tfi++] = dom;


					}

				}

				if (tfi){

					bounds = this.getLimitOffset(offset, limit, tfi);

					offset = bounds[0];

					limit = bounds[1];

					this.onInitCache(cache = {}, fn, query, context);

					result = this[fnName]([toFilter], context, limit, cache);

					this.onClearCache(cache, fn, query, context);

					if (offset){

						return result.slice(offset);

					}

				}

			}

		}

		return result;

	},

	up: function(query, dom, offset, limit){

		var collection = [], ci = 0;

		if (this.Dom.isElement(dom)){

			for(var node = dom; node && node.nodeType == 1; node = node.parentNode){

				collection[ci++] = node;

			}

			if (collection.length){

				return this.filter(collection, query, offset, limit);

			}

		}

		return [];

	},

	before: function(query, dom, offset, limit){

		var collection = [], ci = 0;

		if (this.Dom.isElement(dom)){

			for(var node = dom; node; node = node.previousSibling){

				if (node.nodeType == 1){

					collection[ci++] = node;

				}

			}

			if (collection.length){

				return this.filter(collection, query, offset, limit);

			}

		}

		return [];

	},

	after: function(query, dom, offset, limit){

		var collection = [], ci = 0;

		if (this.Dom.isElement(dom)){

			for(var node = dom.nextSibling; node; node = node.nextSibling){

				if (node.nodeType == 1){

					collection[ci++] = node;

				}

			}

			if (collection.length){

				return this.filter(collection, query, offset, limit);

			}

		}

		return [];

	},

	siblings: function(query, dom, offset, limit){

		var collection = [], ci = 0, pn;

		if (this.Dom.isElement(dom) && (pn = dom.parentNode)){

			for(var node = dom.previousSibling; node; node = node.previousSibling){

				if (node.nodeType == 1){

					collection[ci++] = node;

				}

			}

			for(var node = dom.nextSibling; node; node = node.nextSibling){

				if (node.nodeType == 1){

					collection[ci++] = node;

				}

			}

			if (collection.length){

				return this.filter(collection, query, offset, limit);

			}

		}

		return [];

	},

	previous: function(query, dom, offset, limit){

		if (this.Dom.isElement(dom)){

			return this.before(query, dom.previousSibling, offset, limit);

		}

		return [];

	},

	next: function(query, dom, offset, limit){

		if (this.Dom.isElement(dom)){

			return this.after(query, dom.nextSibling, offset, limit);

		}

		return [];

	},

	parents: function(query, dom, offset, limit){

		if (this.Dom.isElement(dom)){

			return this.up(query, dom.parentNode, offset, limit);

		}

		return [];

	}



});
